package cmd

import (
	"log"
	"os"
	"strings"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/yaml.v3"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

// 新建一个结构体，用于调用初始化k8s client函数
type K8sConfig struct {
	KubeConfigFilePath string  `yaml:"kubeConfigFilePath" `
	ResourceKind       string  `yaml:"resourceKind" `
	Namespace          string  `yaml:"namespace"`
	App                *App    `yaml:"app"`
	Logs               *Logs   `yaml:"logs"`
	Socket             *Socket `yaml:"server"`
	Conf               *Conf   `yaml:"conf"`
}
type Socket struct {
	Host string `yaml:"host"`
	Port string `yaml:"port"`
}
type Logs struct {
	Level   string `yaml:"level"`
	Path    string `yaml:"path"`
	ErrPath string `yaml:"errPath"`
	Format  string `yaml:"format"`
	Rota    Rota   `yaml:"rota"`
}
type Rota struct {
	Switch        string `yaml:"switch"`
	MaxLogSize    int    `yaml:"maxLogSize"`
	MaxLogBackups int    `yaml:"maxlogBackups"`
	MaxLogAge     int    `yaml:"maxLogAge"`
	Compress      bool   `yaml:"compress"`
}

type App struct {
	Version  string         `yaml:"version" json:"version"`
	AppName  string         `yaml:"appName" json:"appName"`
	Describe string         `yaml:"describe" json:"describe"`
	Mode     string         `yaml:"mode" json:"mode"`
	Mapp     map[int]Socket `json:"server"`
}

type Conf struct {
	TimeStep string `yaml:"timeStep" json:"timeStep"`
}

func NewSocket() *Socket {
	return k8sConfig.Socket
}

func GetMode() string {
	return k8sConfig.App.Mode
}

func NewApp() *App {
	//
	k8sConfig.App.Mapp = make(map[int]Socket, 10)
	k8sConfig.App.Mapp[1] = *k8sConfig.Socket
	return k8sConfig.App
}

func GetConf() *Conf {
	return k8sConfig.Conf
}

func NewMyLogger() *zap.Logger {
	if k8sConfig.Logs.Rota.Switch == "open" {
		return NewMyRotaLogger()
	} else {
		return NewMyNoRotaLogger()
	}
}

// 日志轮切
func NewMyRotaLogger() *zap.Logger {
	logConfig := zap.NewProductionEncoderConfig()
	logConfig.TimeKey = "dataTime"
	logConfig.EncodeTime = zapcore.ISO8601TimeEncoder

	infoLogPath := lumberjack.Logger{
		Filename:   k8sConfig.Logs.Path,               // 日志文件路径
		MaxSize:    k8sConfig.Logs.Rota.MaxLogSize,    // 文件大小，单位MB
		MaxBackups: k8sConfig.Logs.Rota.MaxLogBackups, // 保留的备份数
		MaxAge:     k8sConfig.Logs.Rota.MaxLogAge,     // 文件存储的最大天数
		Compress:   true,                              // 是否压缩
	}
	errLogPath := lumberjack.Logger{
		Filename:   k8sConfig.Logs.ErrPath,            // 日志文件路径
		MaxSize:    k8sConfig.Logs.Rota.MaxLogSize,    // 文件大小，单位MB
		MaxBackups: k8sConfig.Logs.Rota.MaxLogBackups, // 保留的备份数
		MaxAge:     k8sConfig.Logs.Rota.MaxLogAge,     // 文件存储的最大天数
		Compress:   true,                              // 是否压缩
	}

	// 配置json日志格式
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,  // 小写编码等级
		EncodeTime:     zapcore.ISO8601TimeEncoder,     // 时间戳编码
		EncodeDuration: zapcore.SecondsDurationEncoder, // 持续时间秒数编码
	}

	// infoLevel := zap.NewAtomicLevelAt(logLevel)
	errPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel
	})
	infoPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.InfoLevel
	})
	warnPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.WarnLevel
	})
	debugPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.DebugLevel
	})

	var core zapcore.Core
	if strings.ToUpper(k8sConfig.Logs.Format) == "JSON" {
		switch strings.ToUpper(k8sConfig.Logs.Level) {
		case "DEBUG":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), debugPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "INFO":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "WARN":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "ERROR":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		default:
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		}
	} else {
		switch strings.ToUpper(k8sConfig.Logs.Level) {
		case "DEBUG":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), debugPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "INFO":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "WARN":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		case "ERROR":
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		default:
			core = zapcore.NewTee(
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), infoPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&infoLogPath), warnPriority),
				zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(&errLogPath), errPriority),
			)
		}
	}
	return zap.New(core)
}

// 日志不轮切
func NewMyNoRotaLogger() *zap.Logger {
	logConfig := zap.NewProductionEncoderConfig()
	logConfig.TimeKey = "dataTime"
	logConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	var logLevel zapcore.Level
	switch strings.ToUpper(k8sConfig.Logs.Level) {
	case "INFO":
		logLevel = zap.InfoLevel
	case "DEBUG":
		logLevel = zap.DebugLevel
	case "WARN":
		logLevel = zap.WarnLevel
	case "ERROR":
		logLevel = zap.ErrorLevel
	default:
		logLevel = zap.InfoLevel
	}

	config := zap.Config{
		Level:             zap.NewAtomicLevelAt(logLevel),
		Development:       false,
		DisableCaller:     false,
		DisableStacktrace: false,
		Sampling:          nil,
		Encoding:          k8sConfig.Logs.Format,
		EncoderConfig:     logConfig,
		OutputPaths: []string{
			k8sConfig.Logs.Path,
		},
		ErrorOutputPaths: []string{
			k8sConfig.Logs.ErrPath,
		},
		InitialFields: nil,
	}
	loggers := zap.Must(config.Build())
	defer loggers.Sync()
	return loggers

}

// 定义一个函数，可以通过包的此函数，返回结构体的指针，去调用初始化client的操作
func NewK8sConfig() *K8sConfig {
	return k8sConfig
}

// 初始化一个k8s配置对象
func (c *K8sConfig) K8sRestConfig() *rest.Config {
	config, err := clientcmd.BuildConfigFromFlags("", k8sConfig.KubeConfigFilePath)
	if err != nil {
		log.Fatal(err)
	}
	return config
}

// 加载配置文件
var k8sConfig *K8sConfig

func load(path string) error {
	result, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	return yaml.Unmarshal(result, &k8sConfig)
}

func init() {
	log.Println("加载配置文件...")
	load("config/suber.yaml")
	log.Println("加载配置文件成功")
	log.Println("初始化日志配置...")
	NewMyRotaLogger()
	log.Println("日志初始化初始化成功")
	log.Printf("日志配置：\n\t级别：%v\n\t日志输出路径：%v\n\t错误日志输出路径：%v\n\t日志输出格式：%v\n",
		k8sConfig.Logs.Level,
		k8sConfig.Logs.Path,
		k8sConfig.Logs.ErrPath,
		k8sConfig.Logs.Format)
}
